package event

import (
	"time"

	"github.com/google/uuid"
	"github.com/sirupsen/logrus"
)

type EventStatus int

func (s EventStatus) String() string {
	switch s {
	case Created:
		return "CREATED"
	case Processing:
		return "PROCESSING"
	case Paused:
		return "PAUSED"
	case Finished:
		return "FINISHED"
	case Stopped:
		return "STOPPED"
	case Failed:
		return "FAILED"
	default:
		return "UNKNOWN"
	}
}

const (
	Created EventStatus = iota
	Processing
	Paused
	Finished
	Stopped
	Failed
)

type EventHistory struct {
	StatusBefore EventStatus
	StatusAfter  EventStatus
	Message      string
	Time         time.Time
}

type EventListener func(*Event)

type Event struct {
	ID        string                 `gorm:"primaryKey;type:varchar(36)"` // Unique identifier for the event
	Name      string                 // Name of the event
	Type      int                    // Type of the event
	Status    EventStatus            // Current status of the event
	Message   string                 // Description or message related to the event
	History   []EventHistory         `gorm:"serializer:json"` // History of changes to the event
	Context   map[string]interface{} `gorm:"serializer:json"` // Context or additional information associated with the event
	listeners []EventListener        `gorm:"-"`               // Listeners or handlers for the event
	Priority  uint8                  `gorm:"-"`

	ParentID string   `gorm:"index"`           // ID of the parent event
	Children []string `gorm:"serializer:json"` // IDs of child events

	SoftwareRunID string `gorm:"index;type:varchar(36)"` // Unique identifier for the software run

	Processor EventProcessor `gorm:"-"` // Current processor, can be nil
}

func (e *Event) GetPriority() uint8  { return e.Priority }
func (e *Event) SetPriority(p uint8) { e.Priority = p }

func NewEvent(evtType int, name string, parent *Event) *Event {
	e := &Event{
		ID:      uuid.NewString(),
		Name:    name,
		Type:    evtType,
		History: make([]EventHistory, 0),
		Context: make(map[string]interface{}),
	}
	if parent != nil {
		parent.AddChild(e)
	}
	return e
}

// add a child
func (e *Event) AddChild(ch *Event) {
	if ch == nil {
		return
	}
	if e.Children == nil {
		e.Children = make([]string, 0)
	}
	e.Children = append(e.Children, ch.ID)
	ch.ParentID = e.ID
}

func (e *Event) addHistory(before, after EventStatus, msg string) {
	if e.History == nil {
		e.History = make([]EventHistory, 0)
	}
	h := EventHistory{
		StatusBefore: before,
		StatusAfter:  after,
		Message:      msg,
		Time:         time.Now(),
	}
	e.History = append(e.History, h)

	logrus.Debugf("%s event '%s': %s->%s, message: %s", h.Time.Format("15:04:05.000"), e.Name, h.StatusBefore, h.StatusAfter, h.Message)

	for _, l := range e.listeners {
		go l(e)
	}
}

// change the status
func (e *Event) ChangeStatus(s EventStatus) {
	e.addHistory(e.Status, s, "")
	e.Status = s
}

// change the message
func (e *Event) ChangeMessage(msg string) {
	e.addHistory(e.Status, e.Status, msg)
	e.Message = msg
}

// change both message and status
func (e *Event) ChangeStatusAndMessage(s EventStatus, msg string) {
	e.addHistory(e.Status, s, msg)
	e.Status = s
	e.Message = msg
}

// the recent history item
func (e *Event) LastHistory() (EventHistory, bool) {
	if len(e.History) > 0 {
		return e.History[len(e.History)-1], true
	}
	return EventHistory{}, false
}

// add a listener
func (e *Event) AddListener(l EventListener) {
	if l == nil {
		return
	}
	if e.listeners == nil {
		e.listeners = make([]EventListener, 0)
	}
	e.listeners = append(e.listeners, l)
}

// whether the event is done
func (e *Event) Done() bool {
	switch e.Status {
	case Finished:
		fallthrough
	case Stopped:
		fallthrough
	case Failed:
		return true
	}
	return false
}
func (e *Event) Finished() bool {
	return e.Status == Finished
}
func (e *Event) Paused() bool {
	return e.Status == Paused
}
func (e *Event) Failed() bool {
	return e.Status == Failed
}
func (e *Event) Stopped() bool {
	return e.Status == Stopped
}
